package Messaging;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

/*
 * @author Roch San Buenaventura
 */

public class NotificationManager 
{
	private HashMap<String, ArrayList<EventCallback>> listsOfEventObservers_ = null;
	private static NotificationManager INSTANCE = null;
	
	Class twoParameters[] = {Object.class, String.class};
	Class threeParameters[] = {Object.class, String.class, boolean.class};
	
	private NotificationManager() 
	{
		listsOfEventObservers_ = new HashMap<String, ArrayList<EventCallback>>();
	}
	
	public static NotificationManager getInstance() 
	{
		if (INSTANCE == null)
			INSTANCE = new NotificationManager();
		
		return INSTANCE;		
	}
	
	private void addObserver(String event, Object subscriber, Method callBackMethod) 
	{
		if (!listsOfEventObservers_.containsKey(event))
			listsOfEventObservers_.put(event, new ArrayList<EventCallback>());
		
		EventCallback observer = new EventCallback(subscriber, callBackMethod);
		ArrayList<EventCallback> list = listsOfEventObservers_.get(event);
		
		if (!list.contains(observer))
			list.add(observer);
		else
			System.err.println(observer + " multiple attempts to subscribe to " + event);
	}

	public void addObserver(String event, Object subscriber, String callbackMethodName) 
	{
		Method callbackMethod = null;

		try 
		{
			callbackMethod = subscriber.getClass().getMethod(callbackMethodName, Object.class);
		}

		catch(Exception e) 
		{
		}

		if (callbackMethod == null)
		{
			try 
			{
				callbackMethod = subscriber.getClass().getMethod(callbackMethodName);
			}
			
			catch(Exception e) 
			{
			}
		}
		
		if (callbackMethod == null)
		{
			try
			{
				callbackMethod = subscriber.getClass().getMethod(callbackMethodName, twoParameters);
			}
			
			catch(Exception e) 
			{
			}
		}

		if (callbackMethod == null) 
		{
			try 
			{
				callbackMethod = subscriber.getClass().getMethod(callbackMethodName, threeParameters);
			}
			
			catch (Exception e) 
			{
			}

			if(callbackMethod == null) 
			{
				String error = "Unable to find " + callbackMethodName + " in class " + subscriber.getClass() + " and has therefore not been added as a " + "subscriber to " + event + " Events";
				System.err.println(error);
			}
		}
		
		addObserver(event, subscriber, callbackMethod);
	}

	public void removeEventObserver(Object obj)
	{
		Collection<ArrayList<EventCallback>> unionObservers = listsOfEventObservers_.values();
		
		for (ArrayList<EventCallback> observerList : unionObservers) 
		{

			for (int i = observerList.size() - 1; i >= 0; i--) 
			{
				EventCallback observer = observerList.get(i);
				if (observer.getObject().equals(obj))
					observerList.remove(i);
			}
		}
	}

	public void removeEventObserver(String event, Object obj) 
	{
		ArrayList<EventCallback> observerList = listsOfEventObservers_.get(event);

		for (int i = observerList.size() - 1; i >= 0; i--) 
		{
			EventCallback observer = observerList.get(i);
			if (observer.getObject().equals(obj))
				observerList.remove(i);
		}
	}

	public void notifyObservers(String event, Object updatedObject) 
	{
		notifyObservers(event, updatedObject, false);
	}

	public void notifyObservers(String event, Object updatedObject, boolean terminal) 
	{
		ArrayList<EventCallback> observerList = listsOfEventObservers_.get(event);

		if (observerList != null) 
		{
			for (int i = observerList.size() - 1; i >= 0; i--)
				observerList.get(i).notifyObserver(updatedObject , event, terminal);
		}
		
		else
			System.err.println("No Observers have ever subscribed to event "+event);
	}
}